using System;
using System.Collections.Generic;
using System.Drawing;
using System.Globalization;
using System.Windows.Forms;
using Plugins.Core;
using Plugins.Core.Interfaces;
using Plugins.Engine;
using Plugins.Interfaces;
using Plugins.Library;

namespace Plugins.Controls
{
    [PluginName("Plugin manager window")]
    [PluginAuthor("hack2root")]
    [PluginDescription("Plugin shows the graphical user interface window for plugins activation")]
    public partial class PluginManagerWindow : PluginWindow, IPluginManagerWindow, IPluginWindow
    {
        private readonly PluginListViewItemComparer _comparer = new PluginListViewItemComparer(0, SortOrder.Ascending);

        private readonly Dictionary<Guid, ListViewGroup> _groups = new Dictionary<Guid, ListViewGroup>();

        private readonly Dictionary<string, ListViewItem> _items = new Dictionary<string, ListViewItem>();

        private static IPluginWindow _pluginManagerForm = new PluginManagerWindow();

        private event EventHandler<PluginEventArgs> _pluginActivated;

        private event EventHandler<PluginEventArgs> _pluginDeactivated;

        public event EventHandler<PluginEventArgs> PluginActivated
        {
            add
            {
                if (!PluginHelper.GetInvocationList(this._pluginActivated, value))
                {
                    this._pluginActivated = (EventHandler<PluginEventArgs>)Delegate.Combine(this._pluginActivated, value);
                }
            }
            remove
            {
                if (PluginHelper.GetInvocationList(this._pluginActivated, value))
                {
                    this._pluginActivated = (EventHandler<PluginEventArgs>)Delegate.Remove(this._pluginActivated, value);
                }
            }
        }

        public event EventHandler<PluginEventArgs> PluginDeactivated
        {
            add
            {
                if (!PluginHelper.GetInvocationList(this._pluginDeactivated, value))
                {
                    this._pluginDeactivated = (EventHandler<PluginEventArgs>)Delegate.Combine(this._pluginDeactivated, value);
                }
            }
            remove
            {
                if (PluginHelper.GetInvocationList(this._pluginDeactivated, value))
                {
                    this._pluginDeactivated = (EventHandler<PluginEventArgs>)Delegate.Remove(this._pluginDeactivated, value);
                }
            }
        }

        public PluginManagerWindow()
        {
            InitializeGraphics();
            InitializeComponent();

            this.NotifyLoad += (object sender, PluginWindowEventArgs e) => { this.LoadWindowState(); };
            this.NotifyUnload += (object sender, PluginWindowEventArgs e) => { this.UnloadWindowState(); };

            pluginsListView.Sorting = SortOrder.Ascending;
            pluginsListView.ListViewItemSorter = _comparer;
        }

        public static void ShowWindow(IWin32Window owner)
        {
            _pluginManagerForm.OpenWindow(owner, false, false);
        }

        private void InitializeGraphics()
        {
            SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            SetStyle(ControlStyles.UserPaint, true);
            SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
        }

        private void pluginsListView_ColumnClick(object sender, ColumnClickEventArgs e)
        {
            pluginsListView.BeginUpdate();
            if (_comparer.ColumnIndex == e.Column)
            {
                switch (_comparer.SortOrder)
                {
                    case SortOrder.Ascending:
                        _comparer.SortOrder = SortOrder.Descending;
                        break;
                    case SortOrder.Descending:
                        _comparer.SortOrder = SortOrder.Ascending;
                        break;
                }
            }
            else
            {
                _comparer.SortOrder = SortOrder.Ascending;
                _comparer.ColumnIndex = e.Column;
            }
            pluginsListView.Sort();
            pluginsListView.EndUpdate();
        }

        public void RegisterHost(IPluginConfigurationHost host)
        {
            RegisterHost(host, DefaultOptions);
        }
        public void UnregisterHost(IPluginConfigurationHost host)
        {
            UnregisterHost(host, DefaultOptions);
        }
        public void RegisterHost(IPluginConfigurationHost host, IPluginOptions options)
        {
            if (host != null)
            {
                PluginControlHelper.Action<PluginManagerWindow>(this, form =>
                {
                    LoadGroup(host);
                    BeginUpdate();
                    IEnumerable<IPluginObject> objects = host.LoadedPlugins;
                    if (objects != null)
                    {
                        foreach (IPluginObject obj in objects)
                        {
                            LoadObject(obj, options, null);
                        }
                    }
                    EndUpdate();
                    PluginControlHelper.Action<ListView>(pluginsListView, listView => listView.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent));
                    IPluginHostContainer container = host as IPluginHostContainer;
                    if (container != null)
                    {
                        container.PluginLoad += host_PluginLoad;
                        container.PluginUnload += host_PluginUnload;
                    }
                });
                PluginControlHelper.Action<ListView>(pluginsListView, listView => listView.Update());
            }
        }
        public void UnregisterHost(IPluginConfigurationHost host, IPluginOptions options)
        {
            if (host != null)
            {
                PluginControlHelper.Action<PluginManagerWindow>(this, form =>
                {
                    IPluginHostContainer container = host as IPluginHostContainer;
                    if (container != null)
                    {
                        container.PluginLoad -= host_PluginLoad;
                        container.PluginUnload -= host_PluginUnload;
                    }
                    BeginUpdate();
                    IEnumerable<IPluginObject> objects = host.LoadedPlugins;
                    if (objects != null)
                    {
                        foreach (IPluginObject obj in objects)
                        {
                            UnloadObject(obj, options, null);
                        }
                    }
                    EndUpdate();
                    UnloadGroup(host);
                });
                PluginControlHelper.Action<ListView>(pluginsListView, listView => listView.Update());
            }
        }
        private void LoadGroup(IPluginConfigurationHost host)
        {
            if (host != null && host.Host != null && host.Host.Id != null)
            {
                if (!_groups.ContainsKey(host.Host.Id))
                {
                    var group = new ListViewGroup(host.Host.Id.ToString(), string.Format(CultureInfo.InvariantCulture, "{{{0}}}", host.Host.Name));
                    PluginLog.WriteAsync(string.Format(CultureInfo.InvariantCulture, "register group={0}", host.Host.Name));
                    _groups.Add(host.Host.Id, group);
                    PluginControlHelper.Action<ListView>(pluginsListView, listView => listView.Groups.Add(group));
                }
            }
        }
        private void UnloadGroup(IPluginConfigurationHost host)
        {
            if (host != null && host.Host != null && host.Host.Id != null)
            {
                if (_groups.ContainsKey(host.Host.Id))
                {
                    var group = (ListViewGroup)_groups[host.Host.Id];
                    PluginLog.WriteAsync(string.Format(CultureInfo.InvariantCulture, "unregister group={0}", host.Host.Name));
                    _groups.Remove(host.Host.Id);
                    PluginControlHelper.Action<ListView>(pluginsListView, listView => listView.Groups.Remove(group));
                }
            }
        }
        private void host_PluginLoad(object sender, PluginHostEventArgs e)
        {
            BeginUpdate();
            LoadObject(e.Plugin, null, null);
            EndUpdate();
        }
        private void host_PluginUnload(object sender, PluginHostEventArgs e)
        {
            BeginUpdate();
            UnloadObject(e.Plugin, null, null);
            EndUpdate();
        }
        private ListViewItem CreateListViewItem(string text)
        {
            ListViewItem item = null;
            PluginControlHelper.Action<PluginManagerWindow>(this, control => item = new ListViewItem(text));
            return item;
        }
        private void LoadObject(IPluginObject plugin, IPluginOptions options, IPluginActivationCollection collection)
        {
            if (plugin != null && plugin.Id != null)
            {
                string guid = GuidHelper.Guid(plugin.Host.Id, plugin.Attributes.Id);
                if (!_items.ContainsKey(guid))
                {
                    PluginLog.WriteAsync(string.Format(CultureInfo.InvariantCulture, "register item={0}", plugin));
                    var item = CreateListViewItem(plugin.Attributes.Name);
                    item.SubItems.Add(plugin.Attributes.Author);
                    item.SubItems.Add(plugin.Attributes.Description);
                    item.SubItems.Add(plugin.Host.Id.ToString());
                    item.SubItems.Add(plugin.Attributes.Id.ToString());
                    item.ToolTipText = plugin.Attributes.Description;
                    item.Group = (ListViewGroup)_groups[plugin.Host.Id];
                    item.Tag = plugin;

                    if (options.Items.Contains(guid))
                    {
                        if (options.Items.GetValue<bool>(guid))
                        {
                            plugin.Activate();
                        }
                        else
                        {
                            plugin.Deactivate();
                        }
                    }
                    item.Checked = plugin.IsActive;
                    _items.Add(guid, item);
                    PluginControlHelper.Action<ListView>(pluginsListView, listView => listView.Items.Add(item));
                }
            }
        }
        private void UnloadObject(IPluginObject plugin, IPluginOptions options, IPluginActivationCollection collection)
        {
            if (plugin != null && plugin.Id != null)
            {
                string guid = GuidHelper.Guid(plugin.Host.Id, plugin.Attributes.Id);
                if (_items.ContainsKey(guid))
                {
                    PluginLog.WriteAsync(string.Format(CultureInfo.InvariantCulture, "unregister item={0}", plugin));
                    var item = (ListViewItem)_items[guid];
                    if (options.Items.Contains(guid))
                    {
                        if (plugin.IsActive)
                        {
                            options.Items.SetValue<bool>(guid, true, true);
                        }
                        else
                        {
                            options.Items.SetValue<bool>(guid, false, false);
                        }
                    }
                    _items.Remove(guid);
                    PluginControlHelper.Action<ListView>(pluginsListView, listView => listView.Items.Remove(item));
                }
            }
        }
        private void BeginUpdate()
        {
            pluginsListView.ItemCheck -= listView1_ItemCheck;
            PluginControlHelper.Action<ListView>(pluginsListView, listView => listView.BeginUpdate());
        }
        private void EndUpdate()
        {
            pluginsListView.ItemCheck += listView1_ItemCheck;
            PluginControlHelper.Action<ListView>(pluginsListView, listView => listView.EndUpdate());
        }

        private void tabControl1_DrawItem(object sender, DrawItemEventArgs e)
        {
            TabPage page = pluginTabsControl.TabPages[e.Index];
            Graphics g = e.Graphics;
            g.FillRectangle(new SolidBrush(page.BackColor), e.Bounds);
            g.DrawString(page.Text, page.Font, new SolidBrush(page.ForeColor), e.Bounds, StringFormat.GenericDefault);
        }
        private void listView1_ItemCheck(object sender, ItemCheckEventArgs e)
        {
            ListViewItem item = pluginsListView.Items[e.Index];
            if (item != null)
            {
                var obj = (IPluginObject)item.Tag;
                if (e.NewValue == CheckState.Checked)
                {
                    if (!obj.IsActive)
                    {
                        PluginLog.WriteAsync(string.Format(CultureInfo.InvariantCulture, "activate item={0}", obj));
                        if (obj.Activate())
                        {
                            OnPluginActivated(obj);
                        }
                    }
                    return;
                }
                if (e.NewValue == CheckState.Unchecked)
                {
                    if (obj.IsActive)
                    {
                        PluginLog.WriteAsync(string.Format(CultureInfo.InvariantCulture, "deactivate item={0}", obj));
                        if (obj.Deactivate())
                        {
                            OnPluginDeactivated(obj);
                        }
                    }
                    return;
                }
            }
        }

        protected void OnPluginActivated(IPluginObject obj)
        {
            if (_pluginActivated != null && obj != null)
            {
                _pluginActivated(this, new PluginEventArgs(obj));
            }
        }
        protected void OnPluginDeactivated(IPluginObject obj)
        {
            if (_pluginDeactivated != null && obj != null)
            {
                _pluginDeactivated(this, new PluginEventArgs(obj));
            }
        }
    }
}